package multithreaded;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import javax.swing.SwingWorker;

import view.View;

import controller.*;


public class ControllerMultiThreaded extends SwingWorker<Void, Void> {
	
	String callPutFlag;
	public double s;
	public double x;
	public double t;
	public double r;
	public double b;
	public double v;
	public int nSteps;
	public int nSimulations;
	View view;
	/**
	 * Constructor
	 * 
	 * @param callPutFlag
	 * @param s
	 * @param x
	 * @param t
	 * @param r
	 * @param b
	 * @param v
	 * @param nSteps
	 * @param nSimulations
	 * @param view
	 */
	public ControllerMultiThreaded(final String callPutFlag, final double s, final double x, final double t,
			double r, final double b, final double v, final int nSteps,int nSimulations, final View view) {
		this.callPutFlag = callPutFlag;
		this.s=s;
		this.x=x;
		this.t=t;
		this.r=r;
		this.b=b;
		this.v=v;
		this.nSteps=nSteps;
		this.nSimulations=nSimulations;
		this.view = view;
		
		
	}
	
	/**
	 * perform the Monte Carlo Simulation in several Threads
	 */
	@Override
	protected Void doInBackground() throws Exception {
		double sum=0;
		
		// start the chrono
		double start = System.currentTimeMillis();
		
		// compute pool size
		final int numberOfCores = Runtime.getRuntime().availableProcessors();
	    final double blockingCoefficient = 0.9;
	    final int poolSize = (int)(numberOfCores / (1 - blockingCoefficient)); //number of threads
	    view.info.setText("Number of Cores:" + numberOfCores+"   Pool size: " + poolSize);
	    System.out.println("Number of Cores:" + numberOfCores+"   Pool size: " + poolSize);
	    
	    final List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
	    final ExecutorService executorPool = Executors.newFixedThreadPool(poolSize);     
	    List<Future<Double>> valueOfStocks = null;
	    
	    // prepare the computation
	    for(int i= 0 ; i< nSimulations ; i++) 
	    {
	      partitions.add(new Callable<Double>() {
					        public Double call() throws Exception 
					        {
					        	return MonteCarloSimulation.oneSimulation(callPutFlag,s, x, t, b, v, nSteps);
					        }        
			      		});
	      view.pb.setValue(Math.round(i*100/nSimulations));
	    }
	    
	  		
	    // perform the computation
		try {
			valueOfStocks = executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);
			
			for(int i=0; i< valueOfStocks.size() ; i++)
			{
				sum += valueOfStocks.get(i).get();
				view.pb.setValue(Math.round(i*100/nSimulations));
				
			}
			
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
	      
	    
	   executorPool.shutdown();
		

	   //perform the average
		double result = MonteCarloSimulation.average(r, t, nSimulations, sum);
		
		//stop the chrono
		double end = System.currentTimeMillis();
		
		
		
		//display result
		System.out.println("MultiThread");
		System.out.println("callPutFlag "+callPutFlag+" s "+s+" x "+x+" t "+t+ " r "+r+
							" b "+b+" v "+v+" nSteps "+ nSteps+ " nSimulations "+nSimulations);
		view.lab.setText("Result : "+result+" in "+(end-start)/1000 + " seconds");
		System.out.println("Result : "+result+" in "+(end-start)/1000 + " seconds");
		view.pb.setValue(100);
		return null;
	}

}
